package com.yfqy.app.modules.wechat.util;

import jakarta.servlet.http.HttpServletRequest;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * XML工具类
 * 提供XML解析、生成和转换功能
 */
public final class XMLUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(XMLUtil.class);
    private static final String XML_DECLARATION = "encoding=\".*\"";
    private static final String UTF8_ENCODING = "encoding=\"UTF-8\"";
    private static final String CDATA_FIELDS = "attach,body,sign";

    private XMLUtil() {
        // 工具类，防止实例化
    }

    /**
     * 解析XML字符串为Map
     *
     * @param xmlStr XML字符串
     * @return 解析后的Map，key为节点名，value为节点值
     * @throws JDOMException XML解析异常
     * @throws IOException   IO异常
     */
    public static Map<String, String> parseXmlToMap(String xmlStr) throws JDOMException, IOException {
        if (!StringUtils.hasText(xmlStr)) {
            return Collections.emptyMap();
        }

        // 统一编码声明
        String normalizedXml = xmlStr.replaceFirst(XML_DECLARATION, UTF8_ENCODING);

        try (InputStream in = new ByteArrayInputStream(normalizedXml.getBytes(StandardCharsets.UTF_8))) {
            SAXBuilder builder = new SAXBuilder();
            Document doc = builder.build(in);
            Element root = doc.getRootElement();

            return root.getChildren().stream()
                    .collect(Collectors.toMap(
                            Element::getName,
                            XMLUtil::getElementValue,
                            (existing, replacement) -> existing // 处理重复key
                    ));
        }
    }

    /**
     * 获取元素值
     */
    private static String getElementValue(Element element) {
        List<Element> children = element.getChildren();
        if (CollectionUtils.isEmpty(children)) {
            return element.getTextNormalize();
        } else {
            return getChildrenXml(children);
        }
    }

    /**
     * 获取子节点的XML字符串
     */
    private static String getChildrenXml(List<Element> children) {
        if (CollectionUtils.isEmpty(children)) {
            return "";
        }

        return children.stream()
                .map(child -> {
                    String name = child.getName();
                    String value = child.getTextNormalize();
                    List<Element> grandChildren = child.getChildren();

                    StringBuilder sb = new StringBuilder();
                    sb.append("<").append(name).append(">");
                    if (!CollectionUtils.isEmpty(grandChildren)) {
                        sb.append(getChildrenXml(grandChildren));
                    }
                    sb.append(value);
                    sb.append("</").append(name).append(">");
                    return sb.toString();
                })
                .collect(Collectors.joining());
    }

    /**
     * 获取HTTP请求参数Map
     */
    public static Map<String, String> getRequestParams(HttpServletRequest request) {
        Map<String, String> params = new HashMap<>();
        Enumeration<String> paramNames = request.getParameterNames();

        while (paramNames.hasMoreElements()) {
            String paramName = paramNames.nextElement();
            String[] paramValues = request.getParameterValues(paramName);

            if (paramValues != null && paramValues.length > 0 && StringUtils.hasText(paramValues[0])) {
                params.put(paramName, paramValues[0]);
            }
        }

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("HTTP请求参数: {}", params);
        }

        return params;
    }

    /**
     * Java对象转换为XML字符串
     */
    public static String convertToXml(Object obj) {
        return convertToXml(obj, StandardCharsets.UTF_8.name());
    }

    /**
     * Java对象转换为XML字符串（指定编码）
     */
    public static String convertToXml(Object obj, String encoding) {
        if (obj == null) {
            return null;
        }

        try {
            JAXBContext context = JAXBContext.newInstance(obj.getClass());
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);

            StringWriter writer = new StringWriter();
            marshaller.marshal(obj, writer);
            return writer.toString();
        } catch (Exception e) {
            LOGGER.error("对象转XML失败", e);
            throw new RuntimeException("XML转换失败", e);
        }
    }

    /**
     * Map转换为微信XML格式字符串
     */
    public static String convertMapToXml(Map<String, String> parameters) {
        if (CollectionUtils.isEmpty(parameters)) {
            return "<xml></xml>";
        }

        StringBuilder xmlBuilder = new StringBuilder();
        xmlBuilder.append("<xml>");

        parameters.forEach((key, value) -> {
            if (value != null) {
                xmlBuilder.append("<").append(key).append(">");
                if (needCdata(key)) {
                    xmlBuilder.append("<![CDATA[").append(value).append("]]>");
                } else {
                    xmlBuilder.append(value);
                }
                xmlBuilder.append("</").append(key).append(">");
            }
        });

        xmlBuilder.append("</xml>");
        return xmlBuilder.toString();
    }

    /**
     * 判断字段是否需要CDATA包装
     */
    private static boolean needCdata(String fieldName) {
        if (!StringUtils.hasText(fieldName)) {
            return false;
        }
        return CDATA_FIELDS.contains(fieldName.toLowerCase());
    }

    /**
     * 安全的XML解析，返回空Map而不是抛出异常
     */
    public static Map<String, String> safeParseXml(String xmlStr) {
        try {
            return parseXmlToMap(xmlStr);
        } catch (Exception e) {
            LOGGER.warn("XML解析失败，返回空Map", e);
            return Collections.emptyMap();
        }
    }

    /**
     * 从HTTP请求的body中读取XML内容并解析
     */
    public static Map<String, String> parseXmlFromRequest(HttpServletRequest request) throws IOException {
        try {
            String xmlContent = request.getReader().lines()
                    .collect(Collectors.joining(System.lineSeparator()));
            return parseXmlToMap(xmlContent);
        } catch (JDOMException e) {
            LOGGER.error("解析请求XML失败", e);
            throw new IOException("XML解析失败", e);
        }
    }
}